/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.document.report;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.common.constant.doc.ConclusionResult;
import cn.ac.nercis.pes.common.constant.evaluation.AssessmentResult;
import cn.ac.nercis.pes.common.constant.evaluation.EvaluationConclusion;
import cn.ac.nercis.pes.common.constant.evaluation.OffsetConclusion;
import cn.ac.nercis.pes.common.constant.evaluation.RiskLevel;
import cn.ac.nercis.pes.common.constant.knowledge.LayerTarget;
import cn.ac.nercis.pes.common.constant.knowledge.TargetDeepness;
import cn.ac.nercis.pes.common.constant.rules.ScoreDecideRule;
import cn.ac.nercis.pes.common.utils.ConvertUtils;
import cn.ac.nercis.pes.common.utils.DataUtils;
import cn.ac.nercis.pes.common.utils.PlaceholderUtils;
import cn.ac.nercis.pes.model.bo.doc.report.evaluation.OverallEvalBO;
import cn.ac.nercis.pes.model.bo.doc.report.evaluation.OverallResultBO;
import cn.ac.nercis.pes.model.bo.doc.report.evaluation.RiskAnalysisBO;
import cn.ac.nercis.pes.model.bo.doc.report.problem.ProblemSuggestBO;
import cn.ac.nercis.pes.model.bo.doc.report.problem.ProblemSuggestItemBO;
import cn.ac.nercis.pes.model.bo.doc.report.stat.TargetAspectBO;
import cn.ac.nercis.pes.model.bo.doc.report.stat.TargetStatBO;
import cn.ac.nercis.pes.model.bo.doc.report.table.OverallEvalReviseBO;
import cn.ac.nercis.pes.model.bo.doc.report.table.OverallResultReportBO;
import cn.ac.nercis.pes.model.bo.doc.report.table.RiskAnalysisReportBO;
import cn.ac.nercis.pes.model.dto.evaluation.analyze.TargetScoreDTO;
import cn.ac.nercis.pes.model.dto.report.OverallEvalResultDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationRecordRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.OverallEvaluationRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.ResultCorrectionRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.UnitRiskAnalysisRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemSpecialTargetRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRecordDO;
import cn.ac.nercis.pes.repository.model.evaluation.ResultCorrectionDO;
import cn.ac.nercis.pes.repository.model.evaluation.UnitRiskAnalysisDO;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.document.common.DocTargetDataService;
import cn.ac.nercis.pes.service.utils.AssetUtils;
import cn.ac.nercis.pes.service.utils.CalculateUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 报告整体测评数据服务类
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class ReportOverallEvaluationDataService {
    private final static String CORRECTION_SCORE_FORMAT = "%s/%s";
    private final static String NEWLINE_CHAR = "\\n";
    private final static Double UNIT_MATCH_SCORE = 1d;
    private final static Double UNIT_NOT_MATCH_SCORE = 0d;

    private final ResultCorrectionRepository resultCorrectionRepository;
    private final EvaluationRecordRepository evaluationRecordRepository;
    private final UnitRiskAnalysisRepository riskAnalysisRepository;
    private final SystemTargetRepository systemTargetRepository;
    private final DocTargetDataService docTargetDataService;
    private final OverallEvaluationRepository overallEvaluationRepository;
    private final SystemSpecialTargetRepository systemSpecialTargetRepository;

    @Autowired
    public ReportOverallEvaluationDataService(ResultCorrectionRepository resultCorrectionRepository,
                                              EvaluationRecordRepository evaluationRecordRepository,
                                              UnitRiskAnalysisRepository riskAnalysisRepository,
                                              SystemTargetRepository systemTargetRepository,
                                              DocTargetDataService docTargetDataService,
                                              OverallEvaluationRepository overallEvaluationRepository,
                                              SystemSpecialTargetRepository systemSpecialTargetRepository) {
        this.resultCorrectionRepository = resultCorrectionRepository;
        this.evaluationRecordRepository = evaluationRecordRepository;
        this.riskAnalysisRepository = riskAnalysisRepository;
        this.systemTargetRepository = systemTargetRepository;
        this.docTargetDataService = docTargetDataService;
        this.overallEvaluationRepository = overallEvaluationRepository;
        this.systemSpecialTargetRepository = systemSpecialTargetRepository;
    }

    public String getEvalResult(@NonNull SystemDO systemDO) {
        try {
            var riskAnalysis = riskAnalysisRepository.findAllBySystemId(systemDO.getId());
            var highSum = riskAnalysis.stream().filter(ra -> ra.getRiskLevel() == RiskLevel.HIGH).count();
            return getEvalResult(systemDO, highSum > 0);
        } catch (RuntimeException rEx) {
            log.error("{}/getEvalResult 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public OverallEvalBO getOverallEvalData(@NonNull SystemDO systemDO){
        try {
            return OverallEvalBO.builder()
                    .reviseTable(getReviseTable(systemDO))
                    .result(getOverallResult(systemDO))
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getOverallEvalData 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public RiskAnalysisBO getRiskAnalysisData(@NonNull SystemDO systemDO) {
        try {
            var riskAnalysis = riskAnalysisRepository.findAllBySystemId(systemDO.getId());
            var highSum = riskAnalysis.stream().filter(ra -> ra.getRiskLevel() == RiskLevel.HIGH).count();
            var mediumSum = riskAnalysis.stream().filter(ra -> ra.getRiskLevel() == RiskLevel.MEDIUM).count();
            var lowSum = riskAnalysis.stream().filter(ra -> ra.getRiskLevel() == RiskLevel.LOW).count();
            String riskResult = RiskLevel.LOW.getName();
            if (highSum > 0) {
                riskResult = RiskLevel.HIGH.getName();
            } else if (mediumSum > 0) {
                riskResult = RiskLevel.MEDIUM.getName();
            }
            return RiskAnalysisBO.builder()
                    .table(getRiskAnalysisTableData(systemDO.getId(), riskAnalysis))
                    .highSum(String.valueOf(highSum))
                    .mediumSum(String.valueOf(mediumSum))
                    .lowSum(String.valueOf(lowSum))
                    .riskResult(riskResult)
                    .evalResult(getEvalResult(systemDO, highSum > 0))
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getRiskAnalysisData 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public String getOverallScore(@NonNull SystemDO systemDO) {
        try {
            return ConvertUtils.scoreToString(getSystemOverallScore(systemDO));
        } catch (RuntimeException rEx) {
            log.error("{}/getOverallScore 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public TargetStatBO getTargetStat(@NonNull SystemDO systemDO) {
        try {
            List<OverallEvalResultDTO> overallEvalResults = findOverallResultData(systemDO);
            long matchSum = overallEvalResults.stream().filter(OverallEvalResultDTO::getMatch).count();
            long partMatchSum = overallEvalResults.stream().filter(OverallEvalResultDTO::getPartMatch).count();
            long notMatchSum = overallEvalResults.stream().filter(OverallEvalResultDTO::getNotMatch).count();
            List<SystemTargetDO> layerTargets = systemTargetRepository.findAllBySystemId(systemDO.getId())
                    .stream()
                    .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                    .collect(Collectors.toList());
            return TargetStatBO.builder()
                    .totalCount(this.docTargetDataService.getBaseTargetCount(systemDO).intValue())
                    .inapplicableCount(this.docTargetDataService.getNotUseTargetCount(systemDO).intValue())
                    .specialCount(this.systemSpecialTargetRepository.findAllBySystemId(systemDO.getId()).size())
                    .accordCount((int) matchSum)
                    .partAccordCount((int) partMatchSum)
                    .notAccordCount((int) notMatchSum)
                    .phyEnv(getTargetAspectData(1, LayerTarget.PHY_ENV, overallEvalResults, layerTargets, systemDO))
                    .netCommunication(getTargetAspectData(2, LayerTarget.NET_COMMUNICATION, overallEvalResults, layerTargets, systemDO))
                    .deviceCompute(getTargetAspectData(3, LayerTarget.DEVICE_COMPUTE, overallEvalResults, layerTargets, systemDO))
                    .appData(getTargetAspectData(4, LayerTarget.APP_DATA, overallEvalResults, layerTargets, systemDO))
                    .manageSystem(getTargetAspectData(5, LayerTarget.MANAGE_SYSTEM, overallEvalResults, layerTargets, systemDO))
                    .personnelManage(getTargetAspectData(6, LayerTarget.PERSONNEL_MANAGE, overallEvalResults, layerTargets, systemDO))
                    .buildRunning(getTargetAspectData(7, LayerTarget.BUILD_RUNNING, overallEvalResults, layerTargets, systemDO))
                    .emergencyHandling(getTargetAspectData(8, LayerTarget.EMERGENCY_HANDLING, overallEvalResults, layerTargets, systemDO))
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getTargetStat 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public String getInapplicableTargetStatRecord(@NonNull SystemDO systemDO){
        var overall = this.docTargetDataService.getNotUseTargetNames(systemDO);
        var record = new StringBuilder(String.format("测评指标确认中不适用项数：%s",overall.size()));
        var overallEvalResults = findOverallResultData(systemDO);
        var layerTargets = systemTargetRepository.findAllBySystemId(systemDO.getId())
                .stream()
                .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                .toList();
        var items = Stream.of(LayerTarget.PHY_ENV,LayerTarget.NET_COMMUNICATION,LayerTarget.DEVICE_COMPUTE,LayerTarget.APP_DATA,
                        LayerTarget.MANAGE_SYSTEM,LayerTarget.PERSONNEL_MANAGE,LayerTarget.BUILD_RUNNING,LayerTarget.EMERGENCY_HANDLING)
                .flatMap(lt->getInapplicableTargetNames(lt,overallEvalResults,layerTargets).stream())
                .toList();
        record.append(String.format("\n现场测评中不适用项数：%s",items.size()));
        var checkResult = items.stream()
                .filter(i->overall.stream().noneMatch(o->StringUtils.equals(o,i)))
                .toList();
        record.append(String.format("\n差异的指标：\n%s",StringUtils.join(checkResult, BaseConstants.NEW_LINE)));
        return record.toString();
    }

    public ProblemSuggestBO getProblemSuggest(@NonNull SystemDO systemDO) {
        try {
            List<SystemTargetDO> layerTargets = systemTargetRepository.findAllBySystemId(systemDO.getId())
                    .stream()
                    .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                    .collect(Collectors.toList());
            var riskAnalysis = this.riskAnalysisRepository.findAllBySystemId(systemDO.getId());
            return ProblemSuggestBO.builder()
                    .phyEnv(getProblemSuggestData(systemDO.getId(), LayerTarget.PHY_ENV, layerTargets, riskAnalysis))
                    .netCommunication(getProblemSuggestData(systemDO.getId(), LayerTarget.NET_COMMUNICATION, layerTargets, riskAnalysis))
                    .deviceCompute(getProblemSuggestData(systemDO.getId(), LayerTarget.DEVICE_COMPUTE, layerTargets, riskAnalysis))
                    .appData(getProblemSuggestData(systemDO.getId(), LayerTarget.APP_DATA, layerTargets, riskAnalysis))
                    .manageSystem(getProblemSuggestData(systemDO.getId(), LayerTarget.MANAGE_SYSTEM, layerTargets, riskAnalysis))
                    .personnelManage(getProblemSuggestData(systemDO.getId(), LayerTarget.PERSONNEL_MANAGE, layerTargets, riskAnalysis))
                    .buildRunning(getProblemSuggestData(systemDO.getId(), LayerTarget.BUILD_RUNNING, layerTargets, riskAnalysis))
                    .emergencyHandling(getProblemSuggestData(systemDO.getId(), LayerTarget.EMERGENCY_HANDLING, layerTargets, riskAnalysis))
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getProblemSuggest 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public List<TargetScoreDTO> getLayerScore(SystemDO systemDO, boolean ignoreEmptyLayer){
        var systemTargets = systemTargetRepository.findAllBySystemId(systemDO.getId());
        var evaluationRecord = findEvalResult(systemDO);
        if(ignoreEmptyLayer){
            return systemTargets.stream()
                    .filter(st->st.getKmDeepness() == TargetDeepness.LAYER.getDeepness()
                            && evaluationRecord.stream().anyMatch(er->StringUtils.equals(er.getLayerTargetId(),st.getKmTargetId())))
                    .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                    .map(st-> TargetScoreDTO.builder()
                            .targetId(st.getKmTargetId())
                            .targetType(st.getKmType())
                            .targetName(st.getKmName())
                            .targetScore(st.getKmWeight().floatValue())
                            .evalScore(CalculateUtils.getLayerScore(evaluationRecord,st,systemTargets))
                            .build())
                    .collect(Collectors.toList());
        }else{
            return systemTargets.stream()
                    .filter(st->st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                    .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                    .map(st-> TargetScoreDTO.builder()
                            .targetId(st.getKmTargetId())
                            .targetType(st.getKmType())
                            .targetName(st.getKmName())
                            .targetScore(st.getKmWeight().floatValue())
                            .evalScore(CalculateUtils.getLayerScore(evaluationRecord,st,systemTargets))
                            .build())
                    .collect(Collectors.toList());
        }
    }

    private String getEvalResult(SystemDO systemDO,boolean highRisk){
        if(highRisk){
            return AssessmentResult.NOT_MATCH.getName();
        }
        double overallScore = getSystemOverallScore(systemDO);
        if(overallScore >= 100){
            return AssessmentResult.MATCH.getName();
        }else if(overallScore >= ScoreDecideRule.MP_THRESHOLD.getValue()){
            return AssessmentResult.PARTIALLY_MATCH.getName();
        }else{
            return AssessmentResult.NOT_MATCH.getName();
        }
    }

    private Double getSystemOverallScore(SystemDO systemDO){
        return CalculateUtils.getOverallScore(getLayerScore(systemDO,true));
    }

    private List<EvaluationRecordDO> findEvalResult(SystemDO systemDO){
        List<EvaluationRecordDO> evaluationRecords = evaluationRecordRepository.findAllBySystemId(systemDO.getId());
        List<ResultCorrectionDO> resultCorrections = resultCorrectionRepository.findAllBySystemId(systemDO.getId())
                .stream()
                .filter(rc->rc.getResult() != OffsetConclusion.DEFAULT)
                .collect(Collectors.toList());
        if(CollectionUtils.isEmpty(evaluationRecords) || CollectionUtils.isEmpty(resultCorrections)){
            return evaluationRecords;
        }
        //修正结果覆盖测评结果
        resultCorrections.forEach(rc-> evaluationRecords.stream()
                .filter(er->StringUtils.equals(er.getId(),rc.getRecord().getId()))
                .findFirst()
                .ifPresent(er->{
                    er.setConclusion(EvaluationConclusion.fromConclusion(rc.getResult().getConclusion()));
                    er.setScore(rc.getScore());
                }));
        return evaluationRecords;
    }

    private TargetAspectBO getTargetAspectData(Integer number, LayerTarget layerTarget, List<OverallEvalResultDTO> overallEvalResults,
                                               List<SystemTargetDO> layerTargets, SystemDO systemDO){
        SystemTargetDO lt = layerTargets.stream()
                .filter(st->StringUtils.equals(st.getKmName(),layerTarget.getName()))
                .findFirst().orElse(null);
        if(Objects.isNull(lt)){
            return getTargetAspectDataByEmpty(number,layerTarget);
        }
        String itemTitle = String.format("%d. 在%s方面", number, layerTarget.getName());
        var layerEvaluation = DataUtils.getFirst(overallEvaluationRepository.findBySystemIdAndLayerTargetId(systemDO.getId(), lt.getKmTargetId()));
        if(Objects.nonNull(layerEvaluation) && StringUtils.isNotBlank(layerEvaluation.getContent())){
            itemTitle = itemTitle.concat("，").concat(layerEvaluation.getContent());
        }
        long matchSum = overallEvalResults.stream().filter(er->StringUtils.equals(er.getLayerTarget(),layerTarget.getName())
                && er.getMatch()).count();
        long partMatchSum = overallEvalResults.stream().filter(er->StringUtils.equals(er.getLayerTarget(),layerTarget.getName())
                && er.getPartMatch()).count();
        long notMatchSum = overallEvalResults.stream().filter(er->StringUtils.equals(er.getLayerTarget(),layerTarget.getName())
                && er.getNotMatch()).count();
        long notApplicableSum = overallEvalResults.stream().filter(er->StringUtils.equals(er.getLayerTarget(),layerTarget.getName())
                && er.getNotApplicable()).count();
        return TargetAspectBO.builder()
                .desc(itemTitle)
                .accordCount((int)matchSum)
                .partAccordCount((int)partMatchSum)
                .notAccordCount((int)notMatchSum)
                .inapplicableCount((int)notApplicableSum)
                .build();
    }


    private List<String> getInapplicableTargetNames(LayerTarget layerTarget,
                                                    List<OverallEvalResultDTO> overallEvalResults,
                                                    List<SystemTargetDO> layerTargets){
        SystemTargetDO lt = layerTargets.stream()
                .filter(st->StringUtils.equals(st.getKmName(),layerTarget.getName()))
                .findFirst().orElse(null);
        if(Objects.isNull(lt)){
            return Collections.emptyList();
        }
        return  overallEvalResults.stream()
                .filter(er->StringUtils.equals(er.getLayerTarget(),layerTarget.getName()) && er.getNotApplicable())
                .map(er->String.format("%s/%s",layerTarget.getName(),er.getUnitTarget()))
                .toList();
    }

    private List<ProblemSuggestItemBO> getProblemSuggestData(String systemId, LayerTarget layerTarget, List<SystemTargetDO> layerTargets,
                                                             List<UnitRiskAnalysisDO> riskAnalysis){
        if(CollectionUtils.isEmpty(layerTargets) || CollectionUtils.isEmpty(riskAnalysis)){
            return getProblemSuggestDataByEmpty();
        }
        SystemTargetDO lt = layerTargets.stream()
                .filter(st->StringUtils.equals(st.getKmName(),layerTarget.getName()))
                .findFirst().orElse(null);
        if(Objects.isNull(lt)){
            return getProblemSuggestDataByEmpty();
        }
        Set<String> unitTargetIds = riskAnalysis.stream()
                .filter(ra->StringUtils.equals(ra.getLayerTargetId(),lt.getKmTargetId()))
                .map(UnitRiskAnalysisDO::getUnitTargetId)
                .collect(Collectors.toSet());
        AtomicInteger number = new AtomicInteger(1);
        List<ProblemSuggestItemBO> data = systemTargetRepository.findAllBySystemIdAndKmTargetIdIn(systemId, unitTargetIds)
                .stream()
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .flatMap(st->getProblemSuggestData(number,lt,st,riskAnalysis).stream())
                .collect(Collectors.toList());
        if(CollectionUtils.isEmpty(data)){
            return getProblemSuggestDataByEmpty();
        }else{
            return data;
        }
    }

    private List<ProblemSuggestItemBO> getProblemSuggestData(AtomicInteger number,SystemTargetDO layerTarget,SystemTargetDO unitTarget,
                                                             List<UnitRiskAnalysisDO> riskAnalysis){
        return riskAnalysis.stream()
                .filter(ra->StringUtils.equals(ra.getLayerTargetId(),layerTarget.getKmTargetId())
                        && StringUtils.equals(ra.getUnitTargetId(),unitTarget.getKmTargetId()))
                .map(ra->ProblemSuggestItemBO.builder()
                        .number(number.getAndIncrement())
                        .problem(ra.getProblem())
                        .suggest(ra.getSuggest())
                        .build())
                .collect(Collectors.toList());
    }

    private TargetAspectBO getTargetAspectDataByEmpty(Integer number,LayerTarget layerTarget){
        return TargetAspectBO.builder()
                .desc(String.format("%d. 在%s方面",number,layerTarget.getName()))
                .build();
    }

    private List<ProblemSuggestItemBO> getProblemSuggestDataByEmpty() {
        return Collections.singletonList(ProblemSuggestItemBO.builder()
                .number(1)
                .problem("无")
                .suggest("无")
                .build());
    }

    private List<OverallEvalReviseBO> getReviseTable(SystemDO systemDO){
        List<ResultCorrectionDO> resultCorrections = resultCorrectionRepository.findAllBySystemId(systemDO.getId())
                .stream()
                .filter(rc->rc.getResult() != OffsetConclusion.DEFAULT)
                .collect(Collectors.toList());
        if(CollectionUtils.isEmpty(resultCorrections)){
            return findReviseTableByEmpty();
        }
        Set<String> layerTargetIds = resultCorrections.stream()
                .map(ResultCorrectionDO::getLayerTargetId)
                .collect(Collectors.toSet());
        AtomicInteger number = new AtomicInteger(1);
        return systemTargetRepository.findAllBySystemIdAndKmTargetIdIn(systemDO.getId(), layerTargetIds)
                .stream()
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .flatMap(kt->getReviseTableData(resultCorrections,kt,number).stream())
                .collect(Collectors.toList());
    }

    private List<OverallEvalReviseBO> getReviseTableData(List<ResultCorrectionDO> resultCorrections,
                                                        SystemTargetDO layerTarget,AtomicInteger number){
        Set<String> unitTargetIds = resultCorrections.stream()
                .filter(rc-> StringUtils.equals(rc.getLayerTargetId(),layerTarget.getKmTargetId()))
                .map(ResultCorrectionDO::getUnitTargetId)
                .collect(Collectors.toSet());
        return systemTargetRepository.findAllBySystemIdAndKmTargetIdIn(layerTarget.getSystemId(), unitTargetIds)
                .stream()
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .flatMap(unitTarget->getReviseTableData(resultCorrections,layerTarget,unitTarget,number).stream())
                .collect(Collectors.toList());
    }

    private List<OverallEvalReviseBO> getReviseTableData(List<ResultCorrectionDO> resultCorrections,
                                                        SystemTargetDO layerTarget,SystemTargetDO unitTarget,
                                                        AtomicInteger number){
        var tableData = resultCorrections.stream()
                .filter(rc->StringUtils.equals(rc.getLayerTargetId(),layerTarget.getKmTargetId())
                        && StringUtils.equals(rc.getUnitTargetId(),unitTarget.getKmTargetId()))
                .map(rc->OverallEvalReviseBO.builder()
                        .number(String.valueOf(number.get()))
                        .layerTarget(layerTarget.getKmName())
                        .unitTarget(unitTarget.getKmName())
                        .asset(AssetUtils.getAssetName(rc.getRecord().getAsset()))
                        .beforeResult(String.format(CORRECTION_SCORE_FORMAT,
                                rc.getRecord().getConclusion().getName(),
                                ConvertUtils.scoreToString(rc.getRecord().getScore(),"")))
                        .afterResult(String.format(CORRECTION_SCORE_FORMAT,
                                rc.getResult().getName(),
                                ConvertUtils.scoreToString(rc.getScore(),"")))
                        .reason(getReviseTableReason(rc.getOffsetRecordId(),rc.getDesc()))
                        .build())
                .collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(tableData)){
            number.getAndIncrement();
        }
        return tableData;
    }

    private String getReviseTableReason(String offsetRecordId,String descTemplate){
        if(StringUtils.isEmpty(offsetRecordId) || StringUtils.isBlank(descTemplate)){
            return StringUtils.EMPTY;
        }
        return this.evaluationRecordRepository.findById(offsetRecordId)
                .map(er-> PlaceholderUtils.builder()
                        .withAssetName(AssetUtils.getAssetName(er.getAsset()))
                        .withAssetScore(ConvertUtils.scoreToString(er.getScore(),""))
                        .build()
                        .renderContent(descTemplate))
                .orElse(descTemplate);
    }

    private OverallResultBO getOverallResult(SystemDO systemDO){
        List<OverallEvalResultDTO> overallEvalResults = findOverallResultData(systemDO);
        long matchSum = overallEvalResults.stream().filter(OverallEvalResultDTO::getMatch).count();
        long partMatchSum = overallEvalResults.stream().filter(OverallEvalResultDTO::getPartMatch).count();
        long notMatchSum = overallEvalResults.stream().filter(OverallEvalResultDTO::getNotMatch).count();
        long notApplicableSum = overallEvalResults.stream().filter(OverallEvalResultDTO::getNotApplicable).count();
        return OverallResultBO.builder()
                .table(getResultTable(overallEvalResults))
                .matchSum(String.valueOf(matchSum))
                .partMatchSum(String.valueOf(partMatchSum))
                .notMatchSum(String.valueOf(notMatchSum))
                .notApplicableSum(String.valueOf(notApplicableSum))
                .build();
    }

    private List<OverallResultReportBO> getResultTable(List<OverallEvalResultDTO> overallEvalResults){
        if(CollectionUtils.isEmpty(overallEvalResults)){
            return getResultTableByEmpty();
        }
        var tableData = overallEvalResults.stream()
                .map(er->OverallResultReportBO.builder()
                        .layerTarget(er.getLayerTarget())
                        .unitTarget(er.getUnitTarget())
                        .match(er.getMatch()?ConclusionResult.MATCH.getTag():"")
                        .partMatch(er.getPartMatch()?ConclusionResult.MATCH.getTag():"")
                        .notMatch(er.getNotMatch()?ConclusionResult.MATCH.getTag():"")
                        .notApplicable( er.getNotApplicable()?ConclusionResult.MATCH.getTag():"")
                        .unitScore(er.getIsApply()?er.getUnitScore():ConclusionResult.NOT_APPLICABLE.getTag())
                        .layerScore(er.getLayerScore())
                        .build())
                .collect(Collectors.toList());
        if(CollectionUtils.isEmpty(tableData)){
            return getResultTableByEmpty();
        }
        return tableData;
    }


    private List<RiskAnalysisReportBO> getRiskAnalysisTableData(String systemId, List<UnitRiskAnalysisDO> riskAnalysis){
        if(CollectionUtils.isEmpty(riskAnalysis)){
            return getRiskAnalysisTableByEmpty();
        }
        Set<String> layerTargetIds = riskAnalysis.stream()
                .map(UnitRiskAnalysisDO::getLayerTargetId)
                .collect(Collectors.toSet());
        AtomicInteger number = new AtomicInteger(1);
        return systemTargetRepository.findAllBySystemIdAndKmTargetIdIn(systemId, layerTargetIds)
                .stream()
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .flatMap(layerTarget->getRiskAnalysisTableData(layerTarget,riskAnalysis,number).stream())
                .collect(Collectors.toList());
    }

    public List<RiskAnalysisReportBO> getRiskAnalysisTableByEmpty(){
        return List.of(RiskAnalysisReportBO.builder()
                .number("")
                .layerTarget("")
                .problem("")
                .threaten("")
                .analyze("")
                .high("")
                .medium("")
                .low("")
                .build());
    }
    private List<RiskAnalysisReportBO> getRiskAnalysisTableData(SystemTargetDO layerTarget,List<UnitRiskAnalysisDO> riskAnalysis,AtomicInteger number){
        var unitTargetIds = riskAnalysis.stream()
                .filter(ra->StringUtils.equals(ra.getLayerTargetId(),layerTarget.getKmTargetId()))
                .map(UnitRiskAnalysisDO::getUnitTargetId)
                .collect(Collectors.toSet());
        return systemTargetRepository.findAllBySystemIdAndKmTargetIdIn(layerTarget.getSystemId(), unitTargetIds)
                .stream()
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .flatMap(unitTarget->getRiskAnalysisTableData(layerTarget,unitTarget,riskAnalysis,number).stream())
                .collect(Collectors.toList());
    }

    private List<RiskAnalysisReportBO> getRiskAnalysisTableData(SystemTargetDO layerTarget,SystemTargetDO unitTarget,List<UnitRiskAnalysisDO> riskAnalysis,AtomicInteger number){
        return riskAnalysis.stream()
                .filter(ra->StringUtils.equals(ra.getLayerTargetId(),layerTarget.getKmTargetId())
                        && StringUtils.equals(ra.getUnitTargetId(),unitTarget.getKmTargetId()))
                .map(ra->RiskAnalysisReportBO.builder()
                        .number(DataUtils.toString(number.getAndIncrement()))
                        .layerTarget(layerTarget.getKmName())
                        .problem(StringUtils.trim(ra.getProblem()))
                        .threaten(getRenderThreatCode(ra.getThreatCode()))
                        .analyze(ra.getAnalysis())
                        .high(ra.getRiskLevel() == RiskLevel.HIGH ? "1" : "0")
                        .medium(ra.getRiskLevel() == RiskLevel.MEDIUM ?  "1" : "0")
                        .low(ra.getRiskLevel() == RiskLevel.LOW ?  "1" : "0")
                        .build())
                .toList();
    }

    private String getRenderThreatCode(String threatCode){
        if(StringUtils.isEmpty(threatCode)){
            return "";
        }
        return String.join(NEWLINE_CHAR, StringUtils.split(threatCode, BaseConstants.MULTI_SPLIT_CHAR));
    }

    private List<OverallEvalResultDTO> findOverallResultData(SystemDO systemDO){
        List<SystemTargetDO> systemTarget = systemTargetRepository.findAllBySystemId(systemDO.getId());
        List<EvaluationRecordDO> evaluationRecord = findEvalResult(systemDO);
        List<ResultCorrectionDO> resultCorrections = resultCorrectionRepository.findAllBySystemId(systemDO.getId())
                .stream()
                .filter(rc->rc.getResult() != OffsetConclusion.DEFAULT)
                .toList();
        return systemTarget.stream()
                .filter(st->st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .flatMap(st->findOverallResultData(st,evaluationRecord,systemTarget,resultCorrections).stream())
                .collect(Collectors.toList());
    }

    private List<OverallEvalResultDTO> findOverallResultData(SystemTargetDO layerTarget,List<EvaluationRecordDO> evaluationRecord,
                                                             List<SystemTargetDO> systemTargets,
                                                             List<ResultCorrectionDO> resultCorrections){
        Double layerScore = CalculateUtils.getLayerScore(evaluationRecord,resultCorrections, layerTarget,systemTargets);
        Set<String> unitTargetIds = systemTargets.stream()
                .filter(st-> StringUtils.equals(st.getKmParentId(),layerTarget.getKmTargetId()))
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .map(SystemTargetDO::getKmTargetId)
                .collect(Collectors.toSet());
        return unitTargetIds.stream()
                .map(uti->{
                    SystemTargetDO unitTarget = systemTargets.stream()
                            .filter(st->StringUtils.equals(st.getKmTargetId(),uti))
                            .findFirst().orElse(null);
                    if(Objects.isNull(unitTarget)){
                        return null;
                    }
                    List<EvaluationRecordDO> unitRecords = evaluationRecord.stream()
                            .filter(er->StringUtils.equals(er.getLayerTargetId(),layerTarget.getKmTargetId())
                                    && StringUtils.equals(er.getUnitTargetId(),uti))
                            .collect(Collectors.toList());
                    var unitResultCorrections = resultCorrections.stream()
                            .filter(rc-> StringUtils.equals(rc.getLayerTargetId(),layerTarget.getKmTargetId())
                                    && StringUtils.equals(rc.getUnitTargetId(),uti)
                                    && Objects.nonNull(rc.getScore()))
                            .toList();
                    Double unitScore = CalculateUtils.getUnitScore(unitRecords,unitResultCorrections);
                    boolean isApply = (unitTarget.getIsApply() && CalculateUtils.getUnitEvalConclusion(unitRecords) != EvaluationConclusion.NOT_APPLICABLE);
                    return OverallEvalResultDTO.builder()
                            .layerTarget(layerTarget.getKmName())
                            .unitTarget(unitTarget.getKmName())
                            .isApply(isApply)
                            .match(isApply && unitScore.equals(UNIT_MATCH_SCORE))
                            .partMatch(isApply && (unitScore > UNIT_NOT_MATCH_SCORE && unitScore < UNIT_MATCH_SCORE))
                            .notMatch(isApply && unitScore.equals(UNIT_NOT_MATCH_SCORE))
                            .notApplicable(!isApply)
                            .unitScore(isApply ? ConvertUtils.scoreToString(unitScore,"") : "/")
                            .layerScore(ConvertUtils.scoreToString(layerScore,""))
                            .build();
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<OverallEvalReviseBO> findReviseTableByEmpty() {
        return List.of(OverallEvalReviseBO.builder()
                .number("")
                .layerTarget("")
                .unitTarget("")
                .asset("")
                .beforeResult("")
                .afterResult("")
                .reason("")
                .build());
    }

    private List<OverallResultReportBO> getResultTableByEmpty(){
        return List.of(OverallResultReportBO.builder()
                        .layerTarget("层面名称")
                        .unitTarget("单元名称")
                        .match("")
                        .partMatch("")
                        .notMatch("")
                        .notApplicable(ConclusionResult.NOT_APPLICABLE.getTag())
                        .unitScore("0")
                        .layerScore("0")
                .build());
    }
}